﻿using RSSReader.Dto;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using System.Windows.Data;

namespace RSSReader
{
    public class FeedList : NotificationObject
    {
        private ObservableCollection<ObservableSyndicationFeed> hiddenFeeds;
        private ReadOnlyObservableCollection<ObservableSyndicationFeed> feeds;

        //Creates the lock object somewhere
        private static object _lock = new object();

        public FeedList()
        {
            this.hiddenFeeds = new ObservableCollection<ObservableSyndicationFeed>();
            this.feeds = new ReadOnlyObservableCollection<ObservableSyndicationFeed>(this.hiddenFeeds);
            //Enable the cross acces to this collection elsewhere
            BindingOperations.EnableCollectionSynchronization(this.feeds, _lock);
        }

        public FeedList(FileInfo persistenceFile)
        {
            if (persistenceFile == null)
            {
                throw new ArgumentNullException("persistenceFile");
            }

            if (persistenceFile.Exists)
            {
                var feedListDto = FeedList.LoadPersistenceData(persistenceFile);

                var observableFeedsList = new List<ObservableSyndicationFeed>(feedListDto.Feeds.Count());
                foreach (var feed in feedListDto.Feeds)
                {
                    observableFeedsList.Add(ObservableSyndicationFeed.Create(feed));
                }
                this.hiddenFeeds = new ObservableCollection<ObservableSyndicationFeed>(observableFeedsList);
            }
            else
            {
                this.hiddenFeeds = new ObservableCollection<ObservableSyndicationFeed>();
            }

            this.feeds = new ReadOnlyObservableCollection<ObservableSyndicationFeed>(this.hiddenFeeds);
            //Enable the cross acces to this collection elsewhere
            BindingOperations.EnableCollectionSynchronization(this.feeds, _lock);
        }

        public ReadOnlyObservableCollection<ObservableSyndicationFeed> Feeds
        {
            get
            {
                return this.feeds;
            }
        }

        public void Register(ObservableSyndicationFeed feed)
        {
            this.hiddenFeeds.Add(feed);
        }

        public ObservableSyndicationFeed Register(Uri uri)
        {
            var loadingFeed = ObservableSyndicationFeed.Create(uri);
            this.hiddenFeeds.Add(loadingFeed);
            return loadingFeed;
        }

        public async Task<ObservableSyndicationFeed> RegisterAsync(Uri uri)
        {
            var loadingFeed = await ObservableSyndicationFeed.CreateAsync(uri);
            this.hiddenFeeds.Add(loadingFeed);
            return loadingFeed;
        }

        public void Unregister(ObservableSyndicationFeed feed)
        {
            this.hiddenFeeds.Remove(feed);
        }

        public static void SavePersistenceData(FileInfo fileInfo, FeedList feedList)
        {
            if (fileInfo == null)
                throw new ArgumentNullException("fileInfo");

            if (feedList == null)
                throw new ArgumentNullException("feedList");

            if (feedList.feeds == null || feedList.feeds.Count == 0)
                fileInfo.Delete();

            using (FileStream fs = new FileStream(fileInfo.FullName, FileMode.Create))
            {
                foreach (var feed in feedList.Feeds)
	            {
                    foreach (var feedItem in feed.Items)
                    {
                        if (feedItem.IsRead)
                            feedItem.MarkOld();
                    }   
	            }

                DataContractSerializer dcs = new DataContractSerializer(typeof(FeedListDto));
                dcs.WriteObject(fs, feedList.GetDto());
            }
        }

        public static FeedListDto LoadPersistenceData(FileInfo fileInfo)
        {
            if (fileInfo == null)
                throw new ArgumentNullException("fileInfo");

            if (fileInfo.Length == 0)
            {
                var emptyFeedListDto = new FeedListDto();
                emptyFeedListDto.Feeds = new List<ObservableSyndicationFeedDto>();
                return emptyFeedListDto;
            }
                
            using (FileStream fs = new FileStream(fileInfo.FullName, FileMode.Open))
            {
                DataContractSerializer dcs = new DataContractSerializer(typeof(FeedListDto));
                return (FeedListDto)dcs.ReadObject(fs);
            }
        }

        public void UpdateAllFeeds()
        {
            foreach (var feed in this.feeds)
            {
                feed.Update();
            }
        }

        public async Task UpdateAllFeedsAsync()
        {
            List<Task> tasks = new List<Task>(this.feeds.Count);

            foreach (var feed in this.feeds)
            {
                tasks.Add(Task.Factory.StartNew(() => feed.UpdateAsync()));
            }

            await Task.WhenAll(tasks);
        }

        private string GetValidFeedFileName(ObservableSyndicationFeed feed)
        {
            char[] invalidChars = Path.GetInvalidFileNameChars();
            string validFilename = string.Concat(feed.Title.Split(invalidChars, StringSplitOptions.RemoveEmptyEntries)) + ".xml";
            return validFilename;
        }

        public FeedListDto GetDto()
        {
            var feedListDto = new FeedListDto();
            feedListDto.Feeds = new List<ObservableSyndicationFeedDto>();

            foreach (var feed in this.feeds)
            {
                feedListDto.Feeds.Add(feed.GetDto());
            }

            return feedListDto;
        }
    }
}
